#install.packages("tidyverse"); install.packages("googlesheets")
rm(list = ls())
library("tidyverse")
library("plotly")
library("janitor")
source("../../funs/conv_to_points.R")
source("../../funs/trade_comp.R")
source("../../funs/adv_value_calc.R")

setwd("~/Documents/fantasy-basketball/data/nba_fantasy_data/")
bballref1920 <- read_csv("nba_fantasy_data - bballref1920.csv")
hashtag20 <- read_csv("nba_fantasy_data - 2020_hashtag_proj_clean.csv")
hashalt19 <- read_csv("nba_fantasy_data - 2019_hashtag_alt_proj.csv")
gsd2_inseason <- read_csv("Fantrax-Players-Gold Standard II.csv")
nba_schedule <- read_csv("nba_fantasy_data - 2019_nba_schedule.csv")
gsd2_contracts <- read_csv("nba_fantasy_data - gsd2_contracts_202021.csv") %>% 
  select(-position)
  
gsd2_inseason <- gsd2_inseason %>% mutate(Trade = Status)

# 2018-19 projections from hashtag 
hashtag20 <- hashtag20 %>% 
  clean_nba_names(player) %>% 
  hashtag_avgs_to_fppg() %>% 
  select(player, contains("fppg"))

gsd2_contracts <- gsd2_contracts %>% 
  clean_nba_names(player)

# clean franchise data 
gsd2_inseason <- gsd2_inseason %>% 
  rename(fntx_pts = "PTS", fntx_orb = "OREB", fntx_dreb = "DREB", 
         fntx_ast = "AST", fntx_stl = "ST", fntx_blk = "BLK", 
         fntx_tov = "TO", fntx_threes = "3PTM", fntx_fga = "FG-", 
         fntx_ftm = "FTM", fntx_gms = "GP", fntx_fppg = "FP/G", fpts = FPts,
         trade_comp = "Trade", 
         nba_team = "Team", gsd2_team = "Status", player = "Player", position = "Position") %>%
  mutate(nba_team = recode(nba_team, PHO = "PHX", SA = "SAS", 
                           NY = "NYK", NO = "NOP", GS = "GSW")) %>% 
  clean_nba_names(player) 

# join all projections 
combined_data <- hashtag20 %>% 
  #full_join(hashtag20, by = "player") %>% 
  full_join(gsd2_contracts, by = "player") %>% 
  full_join(gsd2_inseason, by = "player") %>% 
  arrange(-gsd2_fppg_ht) %>% 
  select(player, gsd2_team, gsd2_fppg_ht, fpts, fntx_fppg, everything(), 
         -ld_fppg_ht, -botb_fppg_ht) 
 
select_data <- combined_data  

# Best FA available 
select_data; select_data %>% filter(gsd2_team == "FA")
## # A tibble: 1,411 x 33
##    player gsd2_team gsd2_fppg_ht  fpts fntx_fppg fr_fppg_ht gsd2_2020 gsd2_2021
##    <chr>  <chr>            <dbl> <dbl>     <dbl>      <dbl>     <dbl>     <dbl>
##  1 James… DAL               51.0 1699       54.8       57.6     30        30   
##  2 Giann… MIL               48.6 2031       58.0       57.9     27.5      30.2 
##  3 Luka … WSH               48.4 1725.      53.9       57.0      8.05     10.2 
##  4 LeBro… MEM               44.2 1774.      49.3       53.4     30        30   
##  5 Damia… LAL               43.5 1623.      47.7       48.8     30        30   
##  6 Antho… NOP               43.4 1016.      44.2       50.8     30.2      30.2 
##  7 Karl-… BOS               43.2  771       48.2       51.6     29.5      30   
##  8 Trae … NYK               42.2 1510.      44.4       47.6      6.57      8.33
##  9 Steph… SAC               40.9 1673.      47.8       46.8     30        30   
## 10 Nikol… GSW               40.4 2128.      59.1       50.4     28.5      30   
## # … with 1,401 more rows, and 25 more variables: gsd2_2022 <dbl>,
## #   gsd2_2023 <dbl>, gsd2_2024 <dbl>, gsd_2025 <dbl>, g_league <dbl>,
## #   nba_team <chr>, position <chr>, Rk <dbl>, Age <dbl>, Opponent <lgl>, `%
## #   Owned` <chr>, `+/-` <chr>, fntx_gms <dbl>, fntx_fga <dbl>,
## #   fntx_threes <dbl>, fntx_ftm <dbl>, `FT-` <dbl>, fntx_pts <dbl>,
## #   fntx_orb <dbl>, fntx_dreb <dbl>, fntx_ast <dbl>, fntx_stl <dbl>,
## #   fntx_blk <dbl>, fntx_tov <dbl>, trade_comp <chr>
## # A tibble: 909 x 33
##    player gsd2_team gsd2_fppg_ht   fpts fntx_fppg fr_fppg_ht gsd2_2020 gsd2_2021
##    <chr>  <chr>            <dbl>  <dbl>     <dbl>      <dbl>     <dbl>     <dbl>
##  1 Jabar… FA               15.9   18         9          19.6      6.5      NA   
##  2 Langs… FA               15.0  181.        7.25       17.4      0.75      0.75
##  3 Wesle… FA               12.8  117.        5.86       15.4     NA        NA   
##  4 Deway… FA               12.4    0         0          16.1      1        NA   
##  5 Ersan… FA               12.3    0         0          15.4     NA        NA   
##  6 Torre… FA               10.9  157         9.24       13.7     NA        NA   
##  7 Thon … FA               10.5   76.8       9.59       13.0     NA        NA   
##  8 Mike … FA                9.82 169.        9.96       12.3     NA        NA   
##  9 Mike … FA                9.82   0         0          12.3     NA        NA   
## 10 Patri… FA                9.7    7.75      2.58       12.8     NA        NA   
## # … with 899 more rows, and 25 more variables: gsd2_2022 <dbl>,
## #   gsd2_2023 <dbl>, gsd2_2024 <dbl>, gsd_2025 <dbl>, g_league <dbl>,
## #   nba_team <chr>, position <chr>, Rk <dbl>, Age <dbl>, Opponent <lgl>, `%
## #   Owned` <chr>, `+/-` <chr>, fntx_gms <dbl>, fntx_fga <dbl>,
## #   fntx_threes <dbl>, fntx_ftm <dbl>, `FT-` <dbl>, fntx_pts <dbl>,
## #   fntx_orb <dbl>, fntx_dreb <dbl>, fntx_ast <dbl>, fntx_stl <dbl>,
## #   fntx_blk <dbl>, fntx_tov <dbl>, trade_comp <chr>

Glimpse at Overall Stats

select_data %>% 
  #filter(player == "Skal Labissiere") %>% # by player
  filter(gsd2_team == "HOU") %>% 
  #filter(trade_comp == "MEM") %>% 
  #filter(bbr_gms > 50) %>%        # by games played
  #filter(bbr_gsd2_fpts > 25) %>%    # by 2018 stats
  #filter(hta_gsd2_fpts > 25) %>%    # by hta projections 
  #filter(ht_gsd2_fpts > 25) %>%     # by ht projections 
  #filter(age < 25) %>%            # by age 
  #arrange(-bbr_gsd2_fpts) %>%       # arrange by 2018 stats
  #arrange(-hta_gsd2_fpts) %>%       # arrange by hta projections
  arrange(-fntx_fppg) %>%         # arrange by ht projections
  select(player, fntx_fppg, gsd2_fppg_ht, #gsd2_2020, gsd2_2021, gsd2_rfa, 
          )
## # A tibble: 18 x 3
##    player            fntx_fppg gsd2_fppg_ht
##    <chr>                 <dbl>        <dbl>
##  1 Rudy Gobert           43.8         33.6 
##  2 Pascal Siakam         39.4         33.2 
##  3 DeMar DeRozan         38.2         31.2 
##  4 LaMelo Ball           35.6         25   
##  5 Kevin Huerter         24.4         17.8 
##  6 Gary Trent Jr.        21.1         15.9 
##  7 Garrett Temple        18.8          9.75
##  8 Grayson Allen         18.6         14.3 
##  9 Pat Connaughton       17.9         12.7 
## 10 Reggie Bullock        16.7         12.4 
## 11 Terance Mann          12.5          2.18
## 12 Reggie Perry          10.7         NA   
## 13 Jalen McDaniels        9.15        11.0 
## 14 Matt Thomas            4.1          4.12
## 15 Tre Jones              2.54        NA   
## 16 Nick Richards          2.5         NA   
## 17 Wenyen Gabriel         2.19         5.9 
## 18 Deividas Sirvydis      0.55        NA

Trade Comparison Tool

source("../../funs/trade_comp.R")

pre_trade_fntx <- pre_trade_calc(df = select_data, league = "gsd2",
                                 current_team = gsd2_team, traded_to = trade_comp, 
                                 fppg_col = fntx_fppg, cap_col = gsd2_2020)
pre_trade_fntx; pre_trade_plot(pre_trade_fntx, var = "team_total", 
                               league = "gsd2", plotly = FALSE)
## # A tibble: 30 x 6
##    team  players avg_fppg team_total cap_used   cap_left
##    <chr>   <int>    <dbl>      <dbl>    <dbl>      <dbl>
##  1 WSH        12     27.8       890.    105.  114399895.
##  2 NYK        12     27.5       878.     93.3 114399907.
##  3 MEM        12     26.3       841.    106.  114399894.
##  4 CLE        12     25.5       815.    109.  114399891.
##  5 ATL        12     25.1       802.    102.  114399898.
##  6 CHI        12     24.9       797.    107.  114399893.
##  7 HOU        12     24.8       794.    101.  114399899.
##  8 BOS        12     24.7       792.    102.  114399898.
##  9 NOP        12     24.7       792.    101.  114399899.
## 10 PHI        12     24.7       789.    104.  114399896.
## # … with 20 more rows

post_trade_fntx <- post_trade_calc(df = select_data, league = "gsd2",
                                   current_team = gsd2_team, traded_to = trade_comp, 
                                   fppg_col = fntx_fppg, cap_col = gsd2_2020)
post_trade_fntx; post_trade_plot(post_trade_fntx, var = "team_total", 
                                 league = "gsd2", plotly = FALSE)
## # A tibble: 30 x 6
##    team  players avg_fppg team_total cap_used   cap_left
##    <chr>   <int>    <dbl>      <dbl>    <dbl>      <dbl>
##  1 WSH        12     27.8       890.    105.  114399895.
##  2 NYK        12     27.5       878.     93.3 114399907.
##  3 MEM        12     26.3       841.    106.  114399894.
##  4 CLE        12     25.5       815.    109.  114399891.
##  5 ATL        12     25.1       802.    102.  114399898.
##  6 CHI        12     24.9       797.    107.  114399893.
##  7 HOU        12     24.8       794.    101.  114399899.
##  8 BOS        12     24.7       792.    102.  114399898.
##  9 NOP        12     24.7       792.    101.  114399899.
## 10 PHI        12     24.7       789.    104.  114399896.
## # … with 20 more rows

pre_trade_fntx <- pre_trade_calc(df = select_data, league = "gsd2",
                                 current_team = gsd2_team, traded_to = trade_comp, 
                                 fppg_col = fntx_fppg, cap_col = gsd2_2020)
pre_trade_fntx; pre_trade_plot(pre_trade_fntx, var = "avg_fppg", 
                               league = "gsd2", plotly = FALSE)
## # A tibble: 30 x 6
##    team  players avg_fppg team_total cap_used   cap_left
##    <chr>   <int>    <dbl>      <dbl>    <dbl>      <dbl>
##  1 WSH        12     27.8       890.    105.  114399895.
##  2 NYK        12     27.5       878.     93.3 114399907.
##  3 MEM        12     26.3       841.    106.  114399894.
##  4 CLE        12     25.5       815.    109.  114399891.
##  5 ATL        12     25.1       802.    102.  114399898.
##  6 CHI        12     24.9       797.    107.  114399893.
##  7 HOU        12     24.8       794.    101.  114399899.
##  8 BOS        12     24.7       792.    102.  114399898.
##  9 NOP        12     24.7       792.    101.  114399899.
## 10 PHI        12     24.7       789.    104.  114399896.
## # … with 20 more rows

post_trade_fntx <- post_trade_calc(df = select_data, league = "gsd2",
                                   current_team = gsd2_team, traded_to = trade_comp, 
                                   fppg_col = fntx_fppg, cap_col = gsd2_2020)
post_trade_fntx; post_trade_plot(post_trade_fntx, var = "avg_fppg", 
                                 league = "gsd2", plotly = FALSE)
## # A tibble: 30 x 6
##    team  players avg_fppg team_total cap_used   cap_left
##    <chr>   <int>    <dbl>      <dbl>    <dbl>      <dbl>
##  1 WSH        12     27.8       890.    105.  114399895.
##  2 NYK        12     27.5       878.     93.3 114399907.
##  3 MEM        12     26.3       841.    106.  114399894.
##  4 CLE        12     25.5       815.    109.  114399891.
##  5 ATL        12     25.1       802.    102.  114399898.
##  6 CHI        12     24.9       797.    107.  114399893.
##  7 HOU        12     24.8       794.    101.  114399899.
##  8 BOS        12     24.7       792.    102.  114399898.
##  9 NOP        12     24.7       792.    101.  114399899.
## 10 PHI        12     24.7       789.    104.  114399896.
## # … with 20 more rows

Advanced Value Calculations

Advanced Value By Player
prediction_data <- adv_value_calc(select_data, 
                                  teams = "gsd2_team", 
                                  pts = "fntx_fppg",
                                  salaries = "gsd2_2020") %>% 
  rowid_to_column("rank")
prediction_data
## # A tibble: 419 x 12
##     rank player teams  fppg lm_pred lm_value lm_norm rf_pred rf_value rf_norm
##    <int> <chr>  <chr> <dbl>   <dbl>    <dbl>   <dbl>   <dbl>    <dbl>   <dbl>
##  1     1 Luka … WSH    53.9    20.4     33.5    3.75    40.0    13.9    2.18 
##  2     2 Bam A… BKN    43.6    17.2     26.4    2.96    35.7     7.84   1.23 
##  3     3 Trae … NYK    44.4    18.8     25.6    2.87    32.9    11.5    1.8  
##  4     4 Shai … OKC    40.5    16.1     24.4    2.73    31.4     9.07   1.42 
##  5     5 Donov… ORL    38.2    17.3     20.9    2.34    31.6     6.60   1.03 
##  6     6 Jarre… GSW    36.4    15.9     20.5    2.29    25.6    10.8    1.70 
##  7     7 Jayso… IND    42.4    22.4     20.0    2.24    31.2    11.2    1.76 
##  8     8 Zion … LAC    42.2    22.8     19.4    2.17    34.0     8.20   1.29 
##  9     9 John … SAC    34.0    16.1     17.9    2.00    31.4     2.57   0.403
## 10    10 Enes … MEM    34.7    17.1     17.6    1.97    21.1    13.6    2.13 
## # … with 409 more rows, and 2 more variables: position <chr>, salaries <dbl>
# fntx residuals 
ggplot(prediction_data, aes(x = salaries, y = fppg)) +
  geom_smooth(method = "lm", se = FALSE, color = "black") +      
  geom_segment(aes(xend = salaries, yend = lm_pred), alpha = .2) +   
  geom_point(aes(color = abs(lm_value), size = abs(lm_value))) +  
  scale_color_continuous(low = "#ffcb0c", high = "#386890") +     
  guides(color = FALSE, size = FALSE) +                             
  geom_point(aes(y = lm_pred), shape = 1) +
  theme_minimal()

prediction_data %>% arrange(-salaries) %>% 
  select(rank, player, salaries, everything())
## # A tibble: 419 x 12
##     rank player salaries teams  fppg lm_pred lm_value lm_norm rf_pred rf_value
##    <int> <chr>     <dbl> <chr> <dbl>   <dbl>    <dbl>   <dbl>   <dbl>    <dbl>
##  1   227 Antho…     30.2 NOP    44.2    44.8   -0.615  -0.069    43.1     1.10
##  2    50 James…     30   DAL    54.8    44.5   10.3     1.15     40.9    13.9 
##  3   106 Kevin…     30   MIN    50.0    44.5    5.44    0.609    40.9     9.02
##  4   116 LeBro…     30   MEM    49.3    44.5    4.75    0.531    40.9     8.33
##  5   150 Steph…     30   SAC    47.8    44.5    3.27    0.366    40.9     6.85
##  6   152 Damia…     30   LAL    47.7    44.5    3.21    0.359    40.9     6.79
##  7   159 Bradl…     30   MIN    47.3    44.5    2.78    0.311    40.9     6.36
##  8   181 Russe…     30   MEM    46.2    44.5    1.68    0.188    40.9     5.26
##  9   188 Jimmy…     30   UTH    45.8    44.5    1.24    0.139    40.9     4.82
## 10   192 Kawhi…     30   ATL    45.4    44.5    0.869   0.097    40.9     4.45
## # … with 409 more rows, and 2 more variables: rf_norm <dbl>, position <chr>
prediction_data %>% 
  filter(fppg > 20) %>% 
  mutate(cost_cats = if_else(salaries < 0.5, 1, 0),
         cost_cats = if_else(salaries > 5 & salaries < 10, 2, cost_cats),
         cost_cats = if_else(salaries > 10 & salaries < 15, 3, cost_cats),
         cost_cats = if_else(salaries > 15 & salaries < 20, 4, cost_cats),
         cost_cats = if_else(salaries > 20 & salaries < 25, 5, cost_cats),
         cost_cats = if_else(salaries > 25, 6, cost_cats)) %>% 
  group_by(cost_cats) %>% 
  summarize(value = sum(lm_norm)) %>% 
  mutate(cost_cats = recode(cost_cats,`0`="UFA",`1`="$<5M",
                            `2`="$5-$9.9M",`3`="$10-14.9M",
                            `4`="$15-19.9+M", `5`="$20-24.9+M", `6`="$25+M"))
## # A tibble: 6 x 2
##   cost_cats  value
##   <chr>      <dbl>
## 1 UFA        63.4 
## 2 $5-$9.9M   43.2 
## 3 $10-14.9M   5.91
## 4 $15-19.9+M  6.24
## 5 $20-24.9+M -2.64
## 6 $25+M      -3.16
Advanced Value By Team
value_by_teams <- team_value_structure(input_data = prediction_data,
                                         teams_col = teams, 
                                         value_col = lm_norm,
                                         fppg_col = fppg)
value_by_teams
## # A tibble: 30 x 14
##    teams  top01  top02  top03 top04 top05 top06 top07 top08 top09 top10 top11
##    <chr>  <dbl>  <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
##  1 NYK    2.87   4.62   5.16   3.71  4.54  5.24  6.57  7.54  8.48  9.24  9.65
##  2 WSH    3.75   3.69   4.74   5.93  6.62  7.78  8.06  7.89  8.24  8.54  8.71
##  3 MEM    0.531  0.719  2.69   4.15  4.85  5.40  6.21  6.24  6.98  6.49  6.83
##  4 SAC    0.366  1.18   2.14   4.14  5.49  5.70  5.52  6.36  6.92  6.36  4.87
##  5 ATL    1.71   1.81   3.58   3.30  4.02  5.01  5.94  6.00  6.48  6.02  5.82
##  6 GSW    1.81   4.11   3.76   3.08  3.62  4.26  5.52  5.52  5.93  5.98  5.54
##  7 OKC    2.73   3.10   3.55   4.81  5.54  5.75  5.67  6.86  6.54  5.91  3.05
##  8 HOU    0.443 -0.128 -0.523  1.33  2.43  3.29  3.98  4.46  4.92  5.43  4.48
##  9 CLE    1.72   2.92   4.64   4.44  4.28  4.26  4.72  5.38  5.18  5.39  5.58
## 10 NOP   -0.069 -0.365  1.12   2.78  3.86  4.46  3.58  4.01  4.70  5.35  5.35
## # … with 20 more rows, and 2 more variables: top12 <dbl>, top13 <dbl>
value_by_players_graph <- value_by_teams %>% 
  #top_n(10, top10) %>% 
  #filter(teams %in% c("NOP", "MIN", "DET", "UTA", "PHX", "PHI")) %>% 
  pivot_longer(!teams, names_to = "players", values_to = "value_extracted") %>% 
  ggplot(aes(x=players, y=value_extracted, group=teams)) +
  geom_line(aes(color=teams))+
  scale_color_manual(values=franchise_team_colors) +
  theme() + theme_minimal(); ggplotly()
prediction_data %>% 
  filter(teams == "MEM")
## # A tibble: 17 x 12
##     rank player teams  fppg lm_pred lm_value lm_norm rf_pred rf_value rf_norm
##    <int> <chr>  <chr> <dbl>   <dbl>    <dbl>   <dbl>   <dbl>    <dbl>   <dbl>
##  1    10 Enes … MEM   34.7     17.1   17.6     1.97    21.1    13.6     2.13 
##  2    31 OG An… MEM   28.9     15.8   13.1     1.46    24.5     4.43    0.695
##  3    80 Derri… MEM   22.6     15.4    7.19    0.805   20.5     2.14    0.335
##  4    88 JaVal… MEM   19.2     12.7    6.58    0.736   11.3     7.94    1.25 
##  5    93 Nicol… MEM   23.4     17.2    6.24    0.699   30.2    -6.83   -1.07 
##  6   111 James… MEM   23.2     18.3    4.91    0.549   18.8     4.33    0.678
##  7   116 LeBro… MEM   49.3     44.5    4.75    0.531   40.9     8.33    1.31 
##  8   157 Raul … MEM   15.7     12.7    3.08    0.345   11.3     4.44    0.697
##  9   181 Russe… MEM   46.2     44.5    1.68    0.188   40.9     5.26    0.824
## 10   207 Lou W… MEM   20.7     20.4    0.339   0.038   25.6    -4.92   -0.773
## 11   234 Marki… MEM   13.4     14.3   -0.958  -0.107   13.6    -0.226  -0.037
## 12   238 Drew … MEM   11.4     12.7   -1.26   -0.141   11.3     0.105   0.015
## 13   289 Tomas… MEM   18.1     22.5   -4.42   -0.494   17.3     0.798   0.124
## 14   373 Jalen… MEM    0.38    12.1  -11.7    -1.31     9.72   -9.34   -1.47 
## 15   393 Jonta… MEM    0       13.2  -13.2    -1.48     9.07   -9.07   -1.42 
## 16   402 Trevo… MEM    0       13.8  -13.8    -1.54    13.8   -13.8    -2.17 
## 17   407 Zhair… MEM    0       15.1  -15.1    -1.69     8.69   -8.69   -1.36 
## # … with 2 more variables: position <chr>, salaries <dbl>
prediction_data %>% 
  select(rank, player, teams, salaries, everything()) %>% 
  filter(teams == "CHA")
## # A tibble: 12 x 12
##     rank player teams salaries  fppg lm_pred lm_value lm_norm rf_pred rf_value
##    <int> <chr>  <chr>    <dbl> <dbl>   <dbl>    <dbl>   <dbl>   <dbl>    <dbl>
##  1    26 Deand… CHA      10.0  36.9     22.6   14.3     1.60    29.9     6.92 
##  2    33 Chris… CHA       6.5  31.3     18.7   12.6     1.40    20.3    11.0  
##  3    86 Fred … CHA      19.2  39.4     32.7    6.72    0.752   34.2     5.24 
##  4   109 Ty Je… CHA       2.3  19.4     14.1    5.29    0.592   18.7     0.719
##  5   174 Jayle… CHA      22.9  38.6     36.7    1.90    0.213   36.5     2.13 
##  6   194 Preci… CHA       1.87 14.4     13.6    0.824   0.092   14.0     0.433
##  7   231 Svi M… CHA       1.66 12.6     13.4   -0.816  -0.091   15.4    -2.79 
##  8   322 Isaac… CHA       1.66  6.95    13.4   -6.44   -0.72    15.4    -8.41 
##  9   327 Damia… CHA       0.75  5.62    12.4   -6.77   -0.757    5.24    0.377
## 10   383 Grant… CHA       0.85  0       12.5  -12.5    -1.40     6.68   -6.68 
## 11   397 Bogda… CHA      18    17.9     31.3  -13.4    -1.50    26.3    -8.38 
## 12   401 LaMar… CHA      24    24.2     37.9  -13.8    -1.54    29.0    -4.86 
## # … with 2 more variables: rf_norm <dbl>, position <chr>
bad_teams <- c("LAL", "DAL", "SAS", "PHX", "ORL", 
               "OKC", "TOR", "BKN", "MIL", "PHI",
               "GSW", "NOP", "CHA", "SAC", "LAC", "CHI")
  
prediction_data %>% 
  filter(teams %in% bad_teams & salaries < 25 & fppg > 20) %>% 
  select(rank, player, teams, salaries, everything()) %>% 
  left_join(select_data %>% select(player, starts_with("gsd2_20")), by = "player") %>% 
  filter(is.na(gsd2_2021)) # expiring contracts 
## # A tibble: 24 x 17
##     rank player teams salaries  fppg lm_pred lm_value lm_norm rf_pred rf_value
##    <int> <chr>  <chr>    <dbl> <dbl>   <dbl>    <dbl>   <dbl>   <dbl>    <dbl>
##  1     6 Jarre… GSW       3.91  36.4    15.9    20.5    2.29     25.6   10.8  
##  2     9 John … SAC       4.14  34.0    16.1    17.9    2.00     31.4    2.57 
##  3    28 Mason… NOP       7.1   32.7    19.4    13.3    1.49     24.9    7.82 
##  4    36 Lauri… SAC       6.73  31      19.0    12.0    1.35     31.1   -0.131
##  5    45 Hamid… OKC       1.66  24.0    13.4    10.6    1.19     15.4    8.66 
##  6    48 Josh … LAC       3.49  25.8    15.4    10.5    1.17     22.0    3.84 
##  7    54 TJ Mc… CHI       3.5   25.0    15.4     9.57   1.07     18.7    6.24 
##  8    58 Micha… MIL       1.5   22.4    13.2     9.14   1.02     12.5    9.82 
##  9    59 Willy… CHI       1     21.7    12.7     9.04   1.01     11.3   10.4  
## 10    62 Dunca… PHX       1.66  22.2    13.4     8.78   0.983    15.4    6.81 
## # … with 14 more rows, and 7 more variables: rf_norm <dbl>, position <chr>,
## #   gsd2_2020 <dbl>, gsd2_2021 <dbl>, gsd2_2022 <dbl>, gsd2_2023 <dbl>,
## #   gsd2_2024 <dbl>